#include "Specialiste.h"

#include <iomanip>

#include "lib.h"

#include "Caracteristique.h"

#include "ListeSorts.h"

#include <fstream>
#include <functional>
#include <algorithm>

#include <boost/assign/list_of.hpp>

using namespace std;
using namespace boost::assign;

const unsigned Specialiste::nbrCompetences( 35 );

const unsigned Specialiste::nbrRestrictions( 5 );

const unsigned Specialiste::nbrRestrictionsUI( 4 );

const unsigned Specialiste::ppAlloues( 30 );

const unsigned Specialiste::nbrSpecialisations( 20 );

const unsigned Specialiste::nbrEcoles( 7 );

/**
 * Contient les écoles de philosophie, les écoles d'effets et
 * les écoles de thaumaturgie.
 */
const vector< vector< unsigned > > Specialiste::ecolesMagie =
  list_of< vector< unsigned > >
  ( list_of(1)(3)(4)(5)(7)(8) ) // Abjurateur
  ( list_of(1)(2)(3)(4)(6)(8) ) // Conjurateur
  ( list_of(1)(2)(3)(4)(5)(7) ) // Divinateur
  ( list_of(1)(2)(3)(4)(5)(6) ) // Enchanteur
  ( list_of(2)(3)(4)(5)(6) ) // Illusionniste
  ( list_of(1)(2)(5)(6)(7)(8) ) // Evocateur
  ( list_of(1)(2)(3)(5)(7)(8) ) // Nécromancien
  ( list_of(2)(3)(4)(5)(6)(7) ) // Transmutateur
  ( list_of(12)(13)(14) ) // Elementaliste de l'Air
  ( list_of(13)(12)(15) ) // Elementaliste de l'Eau
  ( list_of(14)(12)(15) ) // Elementaliste du Feu
  ( list_of(15)(13)(14) ) // Elementaliste de Terre
  ( list_of(1)(2)(3)(5)(6)(7)(11) ) // Dimensionnaliste
  ( list_of(1)(3)(4)(6)(7)(8)(17) ) // Mage des Forces
  ( list_of(2)(3)(4)(5)(6)(8)(20) ) // Mage des Ombres
  ( list_of(1)(2)(3)(4)(5)(7)(9) ) // Alchimiste
  ( list_of(1)(2)(3)(5)(6)(7)(10) ) // Artificier
  ( list_of(1)(2)(3)(4)(5)(16) ) // Eurythmicien
  ( list_of(1)(2)(3)(5)(7)(8)(18) ) // Géométricien
  ( list_of(1)(2)(3)(4)(5)(6)(19) ); // Mentaliste

const vector<CompetenceClasse> Specialiste::Comp = list_of
  (CompetenceClasse("Abjuration"                             ,  5))
  (CompetenceClasse("Altération", "Alteration"               ,  5))
  (CompetenceClasse("Conjuration - Convocation", "Conuration",  5))
  (CompetenceClasse("Divination"                             ,  5))
  (CompetenceClasse("Enchantement - Charme", "Enchantement"  ,  5))
  (CompetenceClasse("Illusion - Fantasme", "Illusion"        ,  5))
  (CompetenceClasse("Invocation - Evocation", "Invocation"   ,  5))
  (CompetenceClasse("Nécromancie", "Necromancie"             ,  5))
  (CompetenceClasse("Alchimie"                               ,  5))
  (CompetenceClasse("Artifices"                              ,  5))
  (CompetenceClasse("Dimension"                              ,  5))
  (CompetenceClasse("Elémentaire - Air", "ElementAir"        ,  5))
  (CompetenceClasse("Elémentaire - Eau", "ElementEau"        ,  5))
  (CompetenceClasse("Elémentaire - Feu", "ElementFeu"        ,  5))
  (CompetenceClasse("Elémentaire - Terre", "ElementTerre"    ,  5))
  (CompetenceClasse("Eurythmique"                            ,  5))
  (CompetenceClasse("Forces"                                 ,  5))
  (CompetenceClasse("Géométrie", "Geometrie"                 ,  5))
  (CompetenceClasse("Mentalisme"                             ,  5))
  (CompetenceClasse("Ombres"                                 ,  5))
  (CompetenceClasse("Bonus à l'étude"          ,  5))
  (CompetenceClasse("Bonus à la recherche"     ,  5))
  (CompetenceClasse("Bonus aux JS"             ,  5))
  (CompetenceClasse("Dés de vie à 1d6"         , 10))
  (CompetenceClasse("Détection de la magie"    , 10))
  (CompetenceClasse("Durées de sorts acrues"   , 10))
  (CompetenceClasse("Incantation rapide"       ,  5))
  (CompetenceClasse("Lecture de la magie"      , 10))
  (CompetenceClasse("Mage combattant"          , 10))
  (CompetenceClasse("Mage en armure"           , 15))
  (CompetenceClasse("Magie intense"            ,  5))
  (CompetenceClasse("Pas de composantes"       , 10))
  (CompetenceClasse("Portée accrue"            , 10))
  (CompetenceClasse("Sort automatique"         ,  5))
  (CompetenceClasse("Sorts supplémentaires"    , 10));

const vector<CompetenceClasse> Specialiste::Restric = list_of
  (CompetenceClasse("Malus à l'étude"                             , 5))
  (CompetenceClasse("Lim : potions, huiles et parchemins"         , 5))
  (CompetenceClasse("Lim : anneau, de bâton, bâtonnet et baguette", 5))
  (CompetenceClasse("Lim : armes et armures magiques"             , 5))
  (CompetenceClasse("Ecoles supplémentaires en opposition"        , 5));

const vector< EnsembleCompetencesClasse > Specialiste::quelSpecialiste = list_of
  ( EnsembleCompetencesClasse( "Abjurateur"      , 0, ecolesMagie[  0 ], 0,  0 ) )
  ( EnsembleCompetencesClasse( "Conjurateur"     , 0, ecolesMagie[  1 ], 1,  1 ) )
  ( EnsembleCompetencesClasse( "Divinateur"      , 0, ecolesMagie[  2 ], 2,  2 ) )
  ( EnsembleCompetencesClasse( "Enchanteur"      , 0, ecolesMagie[  3 ], 2,  3 ) )
  ( EnsembleCompetencesClasse( "Illusioniste"    , 0, ecolesMagie[  4 ], 3,  4 ) )
  ( EnsembleCompetencesClasse( "Invocateur"      , 0, ecolesMagie[  5 ], 0,  5 ) )
  ( EnsembleCompetencesClasse( "Nécromancien"    , 0, ecolesMagie[  6 ], 0,  2 ) )
  ( EnsembleCompetencesClasse( "Transmutateur"   , 0, ecolesMagie[  7 ], 1,  6 ) )
  ( EnsembleCompetencesClasse( "Mage de l'Air"   , 0, ecolesMagie[  8 ], 0,  7 ) )
  ( EnsembleCompetencesClasse( "Mage de l'Eau"   , 0, ecolesMagie[  9 ], 0,  7 ) )
  ( EnsembleCompetencesClasse( "Mage du Feu"     , 0, ecolesMagie[ 10 ], 0,  7 ) )
  ( EnsembleCompetencesClasse( "Mage de Terre"   , 0, ecolesMagie[ 11 ], 0,  7 ) )
  ( EnsembleCompetencesClasse( "Dimensionnaliste", 0, ecolesMagie[ 12 ], 1,  8 ) )
  ( EnsembleCompetencesClasse( "Mage des Forces" , 0, ecolesMagie[ 13 ], 1,  9 ) )
  ( EnsembleCompetencesClasse( "Mage des Ombres" , 0, ecolesMagie[ 14 ], 0, 10 ) )
  ( EnsembleCompetencesClasse( "Alchimiste"      , 0, ecolesMagie[ 15 ], 0, 11 ) )
  ( EnsembleCompetencesClasse( "Artificier"      , 0, ecolesMagie[ 16 ], 3, 12 ) )
  ( EnsembleCompetencesClasse( "Eurythmicien"    , 0, ecolesMagie[ 17 ], 2, 13 ) )
  ( EnsembleCompetencesClasse( "Géométricien"    , 0, ecolesMagie[ 18 ], 2, 14 ) )
  ( EnsembleCompetencesClasse( "Mentaliste"      , 0, ecolesMagie[ 19 ], 0, 19 ) );

const vector< vector< Prerequis > > Specialiste::Limites = 
  list_of< vector< Prerequis > >
  ( list_of( Prerequis(4, 15) ) ) // Abjurateur
  ( list_of( Prerequis(2, 15) ) ) // Conjurateur
  ( list_of( Prerequis(4, 16) ) ) // Divinateur et Nécromancien
  ( list_of( Prerequis(5, 16) ) ) // Enchanteur
  ( list_of( Prerequis(1, 16) ) ) // Illusionniste
  ( list_of( Prerequis(2, 16) ) ) // Evocateur
  ( list_of( Prerequis(1, 15) ) ) // Transmutateur
  ( list_of( Prerequis(3,  9) ) ) // Elementaliste de l'Air
  ( list_of( Prerequis(3,  9) ) ) // Elementaliste de l'Eau
  ( list_of( Prerequis(3,  9) ) ) // Elementaliste du Feu
  ( list_of( Prerequis(3,  9) ) ) // Elementaliste de Terre
  ( list_of( Prerequis(3, 16) ) ) // Dimensionnaliste
  ( list_of( Prerequis(3, 12) )( Prerequis(2, 15) ) ) // Mage des Forces
  ( list_of( Prerequis(3, 15) )( Prerequis(4, 16) ) ) // Mage des Ombres
  ( list_of( Prerequis(3, 15) )( Prerequis(1, 14) ) ) // Alchimiste
  ( list_of( Prerequis(3, 12) )( Prerequis(2, 15) ) ) // Artificier
  ( list_of( Prerequis(3, 14) )( Prerequis(5, 15) ) ) // Eurythmicien
  ( list_of( Prerequis(3, 15) )( Prerequis(4, 14) ) ) // Géométricien
  ( list_of( Prerequis(3, 15) )( Prerequis(4, 16) ) );// Mentaliste

const vector< set< string > > Specialiste::RacesAdmises =
  list_of< set< string > >
  ( list_of("Humain") )                      /* Abjurateur, Evocateur,
						Nécromancien,
						Elementalistes, Mage
						des Ombres, Alchimiste
						et Mentaliste */
  ( list_of("Humain")("Demi-Elfe") )         /*  Conjurateur,
						 Transmutateur,
						 Dimensionnaliste et
						 Mage des Forces */
  ( list_of("Humain")("Demi-Elfe")("Elfe") ) /* Divinateur,
						Enchanteur,
						Eurythmicien et
						Géométricien */
  ( list_of("Humain")("Gnome") );            /* Illusionniste et
						Artificier */

/** Constructeur usuel.
 * Permet la construction d'un spécialiste de A à Z, càd on détermine
 * ici les restrictions, puis les compétences.
 *
 * @param[in] xp le nombre de pts d'expérience du Spécialiste.
 * @param[in] trait les traits du Spécialiste, le 7ème contient le score
 * de force exceptionnelle, fixé à 0 si non applicable.
 * @param[in,out] PP nombre de points de personnage (PP) à disposition
 * pour la création du Spécialiste.
 * @param[in] race nom de la race du personnage.
 */
Specialiste::Specialiste(unsigned xp, const vector< unsigned >& trait,
	   unsigned& PP, const string& race)
  : Classe( xp, Magiciens, "Spécialiste" )
{
  //NomClasse = "Spécialiste";
  NombrePPsAlloues = ppAlloues;
  PP += NombrePPsAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = nbrSpecialisations;
  NombreMaxCompetences    = nbrEcoles;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = Limites;
  RacesAdmisesClasse      = RacesAdmises;
  EnsemblesCompetences    = quelSpecialiste;

  blank() << setfill('*') << setw(56) << '*' << setfill(' ') << endmsg;
  blank() << "Création d'un mage spécialiste" << endmsg << endmsg;

  construitClasse( PP, trait, race );
  /*  
  for (unsigned i(0); i < nbrSpecialisations; i++) // /!\ taille
    blank() << i+1 << ' ' << EnsemblesCompetences[ i ] . Nom << endmsg;
  blank() << endmsg;

  unsigned nbr(0);

  nbr = demanderUnsignedBorne("Choisissez le type de spécialiste ", 1,
				nbrSpecialisations + 1);
  blank() << endmsg;

  if (ecolesMagie[nbr-1][0] > 10)
    // Il s'agit d'un élémentaliste
    {
      debug() << "Elémentaliste" << endmsg;
      for (unsigned j(0); j < 8; j++)
	{
	  Competences.push_back(j);
	  debug() << "Ajout de " << Comp[Competences.back()].Nom << endmsg;
	}
    }

  for (unsigned i(0); i < nbrEcoles; i++)
    {
      if (ecolesMagie[nbr-1][i] != 0)
	Competences.push_back(ecolesMagie[nbr-1][i]-1);
	  debug() << "Ajout de " << Comp[Competences.back()].Nom << endmsg;
    }

  for (unsigned i(0); i < nbrRestrictions; i++)
    {
      if (Restric[i].Cout != 0)
	blank() << i+1 << ' ' << Restric[i].Nom << " ("
		    << Restric[i].Cout << " pp)" << endmsg;
      else
	blank() << i+1 << ' ' << Restric[i].Nom << " cout variable"
		    << endmsg;	
    }
  blank() << nbrRestrictions+1 << " Aucune" << endmsg;

  bool test(true), deja_pris, encore(false);
  unsigned save_PP(PP);

  do
    {
      do
	{
	  nbr = demanderUnsignedBorne("Choisir les restrictions ", 1,
					nbrRestrictions + 1);
	  
	  if (nbr != nbrRestrictions+1)
	    {
	      if (Restric[nbr-1].Cout == 0)
		{
		  deja_pris = safePushBack(&Competences, nbrCompetences + 1 + nbr);
		  if (deja_pris)
		    warning() << "Déjà sélectionné" << endmsg;
		  else
		    {
		      Competences.push_back(nbrCompetences +  nbr - 1);
		      unsigned pps;
		      pps =  demanderUnsigned("Entrez les PP obtenus : ");
		      PP += pps;
		      blank() << "Restriction choisie : "
				  << Restric[nbr-1].Nom << endmsg;
		    }
		}
	      else
		{
		  deja_pris = safePushBack(&Competences, nbrCompetences + 1 + nbr);
		  if (deja_pris)
		    warning() << "Déjà sélectionné" << endmsg;
		  else
		    {
		      Competences.push_back(nbrCompetences + nbr - 1);
		      PP += Restric[nbr-1].Cout;
		      blank() << "Restriction choisie : "
				  << Restric[nbr-1].Nom << endmsg;
		    }
		  if (nbr == 3)
		    {
		      unsigned combien(Competences.size()), laquelle;
		      for (unsigned i(0); i < combien; i++)
			{
			  if (Competences[i] < 20)
			    blank() << setw(2) << i + 1 << ' ' 
					<< Comp[Competences[i]].Nom << endmsg;
			}
		      blank() << combien + 1 << endmsg;
		      
		      laquelle =
			demanderUnsignedBorne("Entrez l'école en opposition supplémentaire", 1, combien);
		      
		      if (laquelle == combien + 1)
			blank() << "Annulation" << endmsg;
		      else
			{
			  linfo() << "Suppression de " << Comp[Competences[laquelle - 1]].Nom << " en cours" << endmsg;
			  vector<unsigned>::iterator it(Competences.begin()), End(Competences.end());
			  debug() << "Avant suppression = " << (unsigned) (End - it) << endmsg;
			  while(*it != laquelle - 1)
			    it++;
			  Competences.erase(it);
			  debug() << "Après suppression = " << (unsigned) (Competences.end() - Competences.begin()) << endmsg;
			}
		      
		    }
		}
	    }
	  else
	    test = false;
	}
      while(test);

      afficheCompetences();
      blank() << endl <<  "1 pour continuer, 2 pour recommencer" << endmsg;
      nbr = demanderUnsignedBorne("Choix ", 1, 2);
      if (nbr == 2)
	{
	  PP = save_PP;
	  Competences.clear();
	  test = true;
	  encore = true;
	  blank() << endmsg;
	}
      else
	encore = false;
    }
  while(encore);

  test = true;

  blank() << "Tu as " << PP << " pp à dépenser" << endmsg;
  save_PP = PP;

  for (unsigned i(20); i < nbrCompetences; i++)
    {
      if (Comp[i].Cout != 0)
	blank() << i+1 << ' ' << Comp[i].Nom << " (" << Comp[i].Cout
		    << " pp)" << endmsg;
      else
	blank() << i+1 << ' ' << Comp[i].Nom << " (cout variable)"
		    << endmsg;	
    }
  blank() << nbrCompetences+1 << " Aucune" << endmsg;
  blank() << "Choisir les compétences de classe" << endmsg;

  test = true;

  do
    {
      do
	{
	  nbr = demanderUnsignedBorne("("+ecrireString(PP)+" restants) ", 20,
					nbrCompetences + 1);
	  if (nbr!=nbrCompetences+1)
	    {
	      if (PP >= Comp[nbr-1].Cout)
		{
		  deja_pris = safePushBack(&Competences, nbr - 1);
		  if (deja_pris)
		    warning() << "Déjà sélectionné" << endmsg;
		  else
		    {
		      Competences.push_back(nbr-1);
		      PP -= Comp[nbr-1].Cout;
		      blank() << "Compétence choisie : " << Comp[nbr-1].Nom
				  << endmsg;
		    }
		}
	      else
		blank() << "Pas assez de pp" << endmsg;
	    }
	  else
	    test = false;
	}
      while(test);

      afficheCompetences();
      blank() << endl <<  "1 pour continuer, 2 pour recommencer" << endmsg;
      nbr = demanderUnsignedBorne("Choix ", 1, 2);
      if (nbr == 2)
	{
	  PP = save_PP;
	  while(Competences.back() < nbrCompetences)
	    Competences.pop_back();
	  test = true;
	  encore = true;
	  blank() << endmsg;
	}
      else
	encore = false;
    }
  while(encore);
  */
  blank() << endmsg << setfill('*') << setw(56) << '*' << endmsg;
  calculNiveau();
  calculNivMag();
}

/** Constructeur simplifié.
 * Permet de construire un Spécialiste lors de la lecture d'un fichier xml.
 *
 * @param[in] xp le nombre de pts d'expérience du Spécialiste.
 * @param[in] trait les traits du Spécialiste, le 7ème contient le score de
 * force exceptionnel, fixé à 0 si non applicable.
 * @param[in] race nom de la race du personnage.
 */
Specialiste::Specialiste( unsigned xp )
  : Classe( xp, Magiciens, "Spécialiste" )
{
  //NomClasse = "Spécialiste";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = nbrSpecialisations;
  NombreMaxCompetences    = nbrEcoles;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = Limites;
  RacesAdmisesClasse      = RacesAdmises;
  EnsemblesCompetences    = quelSpecialiste;

  calculNiveau();
  calculNivMag();
}

/**  Constructeur pour le mode graphique.
 * Permet de construire une instance de Mage avec le contenu
 * minimal, le nombre de points d'expériences et les capacités étant
 * déterminées après la déclaration.
 *
 * @param[in] trait les traits du Mage, le 7ème contient le score de
 * force exceptionnel, fixé à 0 si non applicable.
 * @param[in] UI contrôle, est présent pour des raisons historiques.
 * @param[in] race nom de la race du personnage.
 */
Specialiste::Specialiste( const bool& UI )
  : Classe( 0, Magiciens, "Spécialiste" )
{
  if (!UI)
    {
      error() << "Création du personnage impossible" << endmsg;
      exit(1);
    }

  //NomClasse = "Spécialiste";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = nbrSpecialisations;
  NombreMaxCompetences    = nbrEcoles;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = Limites;
  RacesAdmisesClasse      = RacesAdmises;
  EnsemblesCompetences    = quelSpecialiste;

  calculNiveau();
  calculNivMag();
}

/**
 * Constructeur de "copie", destiné à être utilisé depuis MetaClasse.
 *
 * @param[in] ptr pointeur sur un objet de type Classe (MetaClasse en
 * fait).
 */
Specialiste::Specialiste(Classe *ptr)
  : Classe( *ptr )
{
  NomClasse = "Spécialiste";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = nbrSpecialisations;
  NombreMaxCompetences    = nbrEcoles;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = Limites;
  RacesAdmisesClasse      = RacesAdmises;
  EnsemblesCompetences    = quelSpecialiste;

  calculNiveau();
  calculNivMag();
}

Specialiste::~Specialiste()
{}

void Specialiste::calculNiveau()
{
  if (XP < 60000)
    {
      unsigned i;
      for (i = 0; i < 5; i++)
	{
	  if (XP < (unsigned) 2500 * puissance(2, i))
	    break;
	}
      Niveau = i+1;
    }
  else if (XP < 90000)
    Niveau = 7;
  else if (XP < 135000)
    Niveau = 8;
  else if (XP < 250000)
    Niveau = 9;
  else if (XP < 375000)
    Niveau = 10;
  else if (XP < 750000)
    Niveau = 11;
  else
      Niveau = (XP - 750000) / 375000 + 12;
}

void Specialiste::js(unsigned *tab) const
{
  *(tab + 0) = 14; // Paralysie, Poison ou Mort magique
  *(tab + 1) = 11; // Bâton, batonnet baguette
  *(tab + 2) = 13; // Pétrification ou Métamorphose
  *(tab + 3) = 15; // Souffles
  *(tab + 4) = 12; // Sorts
  
  unsigned cat;
  if (Niveau <= 5)
    cat = 0;
  else  if (Niveau <= 10)
    cat = 1;
  else  if (Niveau <= 15)
    cat = 2;
  else  if (Niveau <= 20)
    cat = 3;
  else
    cat = 4;

  *(tab + 0) -= cat;
  *(tab + 1) -= 2 * cat;
  *(tab + 2) -= 2 * cat;
  *(tab + 3) -= 2 * cat;
  *(tab + 4) -= 2 * cat;
}


unsigned Specialiste::calculeTAc0() const
{
  unsigned mod_niveau((Niveau - 1) / 3);

  return 20 - mod_niveau;
}

void Specialiste::calculNivMag()
{
  if (Niveau < 11)
    NiveauMagique = (Niveau + 1) / 2;
  else if (Niveau == 11)
    NiveauMagique = 5;
  else if (Niveau < 20)
    NiveauMagique = Niveau / 2;
  else
    NiveauMagique = 9;
}

/**
 * Permet de récupérer l'ensemble de capacités raciales désiré.
 *
 * @param[in] nbr numéro de l'ensemble voulu.
 *
 * @return pointeur sur la première capacité de l'ensemble, ou le
 * pointeur nul si on accède à un ensemble inexistant.
 */
const vector<unsigned>* Specialiste::getEnsembleCompetences(const unsigned& nbr) const
{
  if ( nbr > nbrSpecialisations )
    {
      warning() << "Tentative d'accéder à un ensemble inexistant!" << endmsg;
      return 0;
    }
  return &ecolesMagie[ nbr ];
}

/**
 * Il faut ici regarder dans SortsClasse pour savoir quels sont les sorts
 * contenus dans le Livre de Sorts du magicien.
 * 
 * Le tri se fait d'abord par Ecole, puis par Niveau et ensuite dans l'ordre
 * alphabétique.
 *
 * La variable Classe::fonction permet d'afficher :
 *
 * FUNC  : début et fin de l'exécution de la méthode
 *
 * DEBUG : écrasement du fichier, nombre de sorts, sort courant
 */
void Specialiste::ecrireSorts( const string& nom_fichier, const bool& ecrase,
			       const unsigned& ) const
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Specialiste::ecrireSorts -> début" << endmsg;
  ofstream sortie;
  if (ecrase)
    {
      debug() << "Ecrasement du fichier " << nom_fichier << endmsg;
      sortie.open(nom_fichier.c_str());
    }
  else
    {
      sortie.open(nom_fichier.c_str(), ios::app);
      sortie << "\\newpage" << endl;
    }

  sortie << "\\section*{Livre de sorts - Mage}" << endl;

  vector< unsigned > vec;

  sortsParNiveau( vec, -1u );
  
  sortie << "\\begin{center}" << endl 
	 << "\\begin{tabular*}{10cm}{@{\\extracolsep{\\fill}} l r}"
	 << endl << "\\multicolumn{2}{c}{Nombre de sorts par jour} \\\\"
	 << "\\hline \\\\ \\vspace{1mm}" << endl;
  for (unsigned i(0); i < NiveauMagique; i++)
    sortie << "Niveau " << i+1 << " & " << vec[ i ] << " \\\\" << endl;
  sortie << "\\end{tabular*}" << endl << "\\end{center}" << endl;

  unsigned taille( nbrSortsConnus() ), i, courant(10), niv_courant(10);
  string nomComplet, nomAdresse;

  debug() << "nbr de sorts = " << taille << endmsg;

  for(i = 0; i < taille; i++)
    {
      if (SortsClasse[i].ecole() != courant)
	{
	  courant = SortsClasse[i].ecole();
	  niv_courant = SortsClasse[i].niveau();
	  nomComplet = ListesCompetences[0][courant].nom();
	  nomAdresse = ListesCompetences[0][courant].aliasNom();
	  // courant garde en mémoire l'école courante
	  // niv_courant garde en mémoire le niveau courant
	  sortie << "\\subsection*{" << nomComplet << "}" << endl;
	  sortie << "\\subsubsection*{Niveau " << niv_courant << "}" << endl;
	}
      if (SortsClasse[i].niveau() != niv_courant)
	{
	  niv_courant = SortsClasse[i].niveau();
	  sortie << "\\subsubsection*{Niveau " << niv_courant << "}" << endl;
	}

      sortie << "\\input{../../Sorts-mage/" << nomAdresse << "/sort_"
	     << niv_courant << "_" << SortsClasse[i].numero() << ".inc.tex}" << endl;
    }

  sortie.close();
  func() << "Specialiste::ecrireSorts -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

void Specialiste::sortsParNiveau( vector< unsigned >& sorts,
				  const unsigned& ) const
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Specialiste::nbrSort -> début" << endmsg;
  
  //vector< unsigned > tmp( 9, 0 );
  func() << "Niveau de magie = " << NiveauMagique << endmsg;

  switch(Niveau)
    {
    case 1:
      sorts = list_of( 1 );
      //tmp[0] = 1;
      break;
    case 2:
      sorts = list_of( 2 );
      //tmp[0] = 2;
      break;
    case 3:
      sorts = list_of( 2 )( 1 );
      //tmp[0] = 2;
      //tmp[1] = 1;
      break;
    case 4:
      sorts = list_of( 3 )( 2 );
      //tmp[0] = 3;
      //tmp[1] = 2;
      break;
    case 5:
      sorts = list_of( 4 )( 2 )( 1 );
      //tmp[0] = 4;
      //tmp[1] = 2;
      //tmp[2] = 1;
      break;
    case 6:
      sorts = list_of( 4 )( 2 )( 2 );
      //tmp[0] = 4;
      //tmp[1] = 2;
      //tmp[2] = 2;
      break;
    case 7:
      sorts = list_of( 4 )( 3 )( 2 )( 1 );
      //tmp[0] = 4;
      //tmp[1] = 3;
      //tmp[2] = 2;
      //tmp[3] = 1;
      break;
    case 8:
      sorts = list_of( 4 )( 3 )( 3 )( 2 );
      //tmp[0] = 4;
      //tmp[1] = 3;
      //tmp[2] = 3;
      //tmp[3] = 2;
      break;
    case 9:
      sorts = list_of( 4 )( 3 )( 3 )( 2 )( 1 );
      //tmp[0] = 4;
      //tmp[1] = 3;
      //tmp[2] = 3;
      //tmp[3] = 2;
      //tmp[4] = 1;
      break;
    case 10:
      sorts = list_of( 4 )( 4 )( 3 )( 2 )( 2 );
      //tmp[0] = 4;
      //tmp[1] = 4;
      //tmp[2] = 3;
      //tmp[3] = 2;
      //tmp[4] = 2;
      break;
    case 11:
      sorts = list_of( 4 )( 4 )( 4 )( 3 )( 3 );
      //tmp[0] = 4;
      //tmp[1] = 4;
      //tmp[2] = 4;
      //tmp[3] = 3;
      //tmp[4] = 3;
      break;
    case 12:
      sorts = list_of( 4 )( 4 )( 4 )( 4 )( 4 )( 1 );
      //tmp[0] = 4;
      //tmp[1] = 4;
      //tmp[2] = 4;
      //tmp[3] = 4;
      //tmp[4] = 4;
      //tmp[5] = 1;
      break;
    case 13:
      sorts = list_of( 5 )( 5 )( 5 )( 4 )( 4 )( 2 );
      //tmp[0] = 5;
      //tmp[1] = 5;
      //tmp[2] = 5;
      //tmp[3] = 4;
      //tmp[4] = 4;
      //tmp[5] = 2;
      break;
    case 14:
      sorts = list_of( 5 )( 5 )( 5 )( 4 )( 4 )( 2 )( 1 );
      //tmp[0] = 5;
      //tmp[1] = 5;
      //tmp[2] = 5;
      //tmp[3] = 4;
      //tmp[4] = 4;
      //tmp[5] = 2;
      //tmp[6] = 1;
      break;
    case 15:
      sorts = list_of( 5 )( 5 )( 5 )( 5 )( 5 )( 2 )( 1 );
      //tmp[0] = 5;
      //tmp[1] = 5;
      //tmp[2] = 5;
      //tmp[3] = 5;
      //tmp[4] = 5;
      //tmp[5] = 2;
      //tmp[6] = 1;
      break;
    case 16:
      sorts = list_of( 5 )( 5 )( 5 )( 5 )( 5 )( 3 )( 2 )( 1 );
      //tmp[0] = 5;
      //tmp[1] = 5;
      //tmp[2] = 5;
      //tmp[3] = 5;
      //tmp[4] = 5;
      //tmp[5] = 3;
      //tmp[6] = 2;
      //tmp[7] = 1;
      break;
    case 17:
      sorts = list_of( 5 )( 5 )( 5 )( 5 )( 5 )( 3 )( 3 )( 2 );
      //tmp[0] = 5;
      //tmp[1] = 5;
      //tmp[2] = 5;
      //tmp[3] = 5;
      //tmp[4] = 5;
      //tmp[5] = 3;
      //tmp[6] = 3;
      //tmp[7] = 2;
      break;
    case 18:
      sorts = list_of( 5 )( 5 )( 5 )( 5 )( 5 )( 3 )( 3 )( 2 )( 1 );
      //tmp[0] = 5;
      //tmp[1] = 5;
      //tmp[2] = 5;
      //tmp[3] = 5;
      //tmp[4] = 5;
      //tmp[5] = 3;
      //tmp[6] = 3;
      //tmp[7] = 2;
      //tmp[8] = 1;
      break;
    case 19:
      sorts = list_of( 5 )( 5 )( 5 )( 5 )( 5 )( 3 )( 3 )( 3 )( 1 );
      //tmp[0] = 5;
      //tmp[1] = 5;
      //tmp[2] = 5;
      //tmp[3] = 5;
      //tmp[4] = 5;
      //tmp[5] = 3;
      //tmp[6] = 3;
      //tmp[7] = 3;
      //tmp[8] = 1;
      break;
    default:
      sorts = list_of( 5 )( 5 )( 5 )( 5 )( 5 )( 4 )( 3 )( 3 )( 2 );
      //tmp[0] = 5;
      //tmp[1] = 5;
      //tmp[2] = 5;
      //tmp[3] = 5;
      //tmp[4] = 5;
      //tmp[5] = 4;
      //tmp[6] = 3;
      //tmp[7] = 3;
      //tmp[8] = 2;
      break;
    }

  if ( find( Competences.begin(), Competences.end(), 34 ) != Competences.end() )
    for_each( sorts.begin(), sorts.end(), bind1st( plus< unsigned >(), 1 ) );

  //sorts.assign( tmp.begin(), tmp.end() );
  sorts.resize( NiveauMagique );

  func() << "Specialiste::nbrSort -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 *
 * Permet de gérer l'ajout de sorts au livre du magicien
 *
 * La variable Classe::fonction permet d'afficher
 *
 * FUNC  : les messages de début - fin de la méthode
 *
 * LFUNC : les écoles auxquelles le Spécialiste a accès
 *
 * DEBUG : numéro du dernier sort, sort écrit, sorts triés
 */
void Specialiste::ajoutSorts()
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Specialiste::ajoutSorts -> début" << endmsg;

  bool ecoles[20] = {false, false, false, false, false, false, false, false,
		     false, false, false, false, false, false, false, false,
		     false, false, false, false};
  unsigned taille( tailleCompetences() ), i, tmp;
  bool sauter( false );

  for (i = 0; i < taille; i++)
    {
      tmp = Competences[i];
      debug() << "tmp = " << tmp << endmsg;
	if (tmp < 20)
	{
	  lfunc() << nomCompetence(i, sauter) << " école" << endmsg;
	  ecoles[tmp] = true;
	  linfo() << nomCompetence(i, sauter) << " (" 
		  << i << ") oui" << endmsg;
	}
	else
	  lfunc() << nomCompetence(tmp, sauter) << " pas école"
		      << endmsg;
    }

  unsigned nbr, dernier;
  string ecole;

  debug() << "Nombre de sorts : " << nbrSortsConnus() << endmsg;

  debug() << "Niveau magique = " << NiveauMagique << endmsg;

  for (i = 0; i < 20; i++)
    {
      if (!ecoles[i])
	continue;
      blank() << Comp[i].nom() << endmsg;
      for (unsigned j(1); j <= NiveauMagique; j++)
	{
	  blank() << "Niveau " << j << " :" << endmsg;
	  dernier = afficheSorts(i, j);

	  debug() << "dernier = " << dernier << endmsg;
	
	  if (dernier > 0)
	    {
	      blank() << setw(2) << dernier + 1 << " Aucun" << endmsg;
	      do
		{
		  nbr = demanderUnsignedBorne("Entrer nouveau sort : ", 1,
						dernier + 1);
		  if (nbr != dernier + 1)
		    {
		      debug() << "Ajout du sort " << Sort(i, j, nbr - 1)
				  << endmsg;
		      SortsClasse.push_back(Sort(i, j, nbr - 1));
		    }
		}
	      while(nbr != dernier + 1);
	    }
	}
    }

  if ( nbrSortsConnus() == 0)
    return;

  debug() << "Nombre de sorts : " << nbrSortsConnus() << endmsg;

  vector<Sort>::iterator Begin, End, it;
  Begin = SortsClasse.begin();
  End   = SortsClasse.end();

  for (it = Begin; it < End; it++)
    debug() << *it << endmsg;

  sort(Begin, End);

  debug() << "Tri effectué" << endmsg;

  it = unique(Begin, End);

  SortsClasse.erase( it, End );

  for (i = 0; i < SortsClasse.size(); i++)
    debug() << SortsClasse[i] << endmsg;

  func() << "Specialiste::ajoutSorts -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Permet de gérer les compétences martiales, càd lesquelles sont accessibles
 * à quel coût.
 *
 * Un Spécialiste peut prendre un arme réservée au Mage pour 3 pp, ou une arme
 * de Clerc ou de Voleur pour 5 pp et enfin une arme de Guerrier pour 6 pp.
 */
void Specialiste::prixAcces(map<string, unsigned>*& Map) const
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Specialiste::prixAcces -> début" << endmsg;

  map<string, unsigned>::const_iterator mapIt, mapBegin(Map -> begin()),
    mapEnd(Map -> end());

  for (mapIt = mapBegin; mapIt != mapEnd; mapIt++)
    lfunc() << mapIt -> first << endmsg;

  Map -> operator[]("Spécialiste") = 3;
  mapIt = Map -> find("Clerc");
  if ((mapIt != mapEnd && mapIt -> second > 5) || mapIt == mapEnd)
    Map -> operator[]("Clerc")    = 5;
  mapIt = Map -> find("Voleur");
  if ((mapIt != mapEnd && mapIt -> second > 5) || mapIt == mapEnd)
    Map -> operator[]("Voleur")   = 5;
  mapIt = Map -> find("Guerrier");
  if ((mapIt != mapEnd && mapIt -> second > 6) || mapIt == mapEnd)
    Map -> operator[]("Guerrier") = 6;
  
  func() << "Specialiste::prixAcces -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

unsigned Specialiste::aSpecialisation() const
{
  return 0;
}
